
#include <cegfx/cemacros.h>
#include <cegfx/ceparticelnode.h>
#include <cegfx/cevirtualclock.h>



ceParticelNode::ceParticelNode ()
	: ceGeometryNode()
	,	generate (true)
	, mesh (0)
	, boundingBoxUpdate (0)
	, createTime (ceVClock::GetMilliseconds())
{
	IQF_CONSTRUCT;
	SetTransparency (true);
}

ceParticelNode::~ceParticelNode ()
{
}

IQF_IMPLEMENTATION_BEGIN(ceParticelNode);
IQF_IMPLEMENTATION_END ();

bool ceParticelNode::Configure (LPDIRECT3DDEVICE9 device, const Configuration& config)
{
	if (mesh)
	{
		mesh->Release ();
	}
	mesh = new ceParticelMesh ();
	if (!mesh->CreateParticel (device, config.ParticelDimension.x, config.ParticelDimension.y))
	{
		delete mesh;
		return false;
	}

	mesh->AddRef ();

	SetGeometry (mesh);
	SetMaterial (config.Material);

	this->config = config;
	this->createInterval = 1000.0f / (float)config.ParticelsPerSecond;
	if (!this->config.BoundingBoxUpdateInterval)
	{
		this->config.BoundingBoxUpdateInterval = 1;
	}
	if (!this->config.MaxParticels)
	{
		this->config.MaxParticels = this->config.ParticelsPerSecond * (this->config.TimeToLive + this->config.TimeToLiveVariance) / 1000;
	}
	mesh->SetBoundingBox (config.Pos - config.PosVariance, config.Pos + config.PosVariance);
	return true;
}

void ceParticelNode::SetEmitting (bool emitting)
{
	generate = emitting;
	createTime = ceVClock::GetMilliseconds();
}

bool ceParticelNode::IsEmitting () const
{
	return generate;
}


static float GetRandom (float l, float h)
{
	return l + ((float)rand () / (float)RAND_MAX) * (h - l);
}

void ceParticelNode::Update (const ceUpdateEnv& env)
{

	FOR_VECTOR(unsigned, i, j, particels)
	{
		Particel& particel = particels [i];
		particel.timeToLive -= env.DeltaTime;
		if (particel.timeToLive < 0)
		{
			if (generate)
			{
				initializeParticel (particel);
			}
			else
			{
				particels.erase (particels.begin() + i);
				mesh->RemoveParticel (i);
				i--;
				j--;
				continue;
			}
		}

		particel.dir += config.Gravity * env.DeltaTime / 1000.0f;
		particel.pos += particel.dir * env.DeltaTime / 1000.0f;
		particel.rotation += particel.rotationSpeed;
			
		float alpha = 1.0f;
		if (particel.timeToLive < particel.alphaBlendTime)
		{
			alpha = particel.timeToLive / particel.alphaBlendTime;
		}
		mesh->UpdateParticel (i, particel.pos, D3DXVECTOR2(0, 0), particel.rotation, alpha);
	}

	if (this->particels.size() < config.MaxParticels && generate)
	{

		// evaluate the number of particels to create

		float currentTime = ceVClock::GetMilliseconds ();
		float delta = currentTime - createTime;

		unsigned num = delta / this->createInterval;
		if (num + particels.size () > this->config.MaxParticels)
		{
			num = this->config.MaxParticels - particels.size();
		}

		if (num > 0)
		{
			for (unsigned i=0; i<num; i++)
			{
				Particel p;
				initializeParticel (p);

				mesh->AddParticel (p.pos);
				particels.push_back (p);
			}
			createTime = currentTime;
		}
	}

	if ((boundingBoxUpdate % config.BoundingBoxUpdateInterval) == 0)
	{
		mesh->UpdateBoundingBox();
		UpdateBoundingBox();
	}

	boundingBoxUpdate++;
}

void ceParticelNode::initializeParticel (Particel& p)
{
	p.pos = config.Pos + D3DXVECTOR3(
			GetRandom (-config.PosVariance.x, config.PosVariance.x),
			GetRandom (-config.PosVariance.y, config.PosVariance.y),
			GetRandom (-config.PosVariance.z, config.PosVariance.z));

		p.dir = config.Direction + D3DXVECTOR3(
			GetRandom (-config.DirectionVariance.x, config.DirectionVariance.x),
			GetRandom (-config.DirectionVariance.y, config.DirectionVariance.y),
			GetRandom (-config.DirectionVariance.z, config.DirectionVariance.z));

		p.timeToLive = (float)config.TimeToLive + 
			GetRandom (-(float)config.TimeToLiveVariance, (float)config.TimeToLiveVariance);

		p.rotation = 0.0f;
		p.rotationSpeed = config.Rotation + 
			GetRandom (-(float)config.RotationVariance, (float)config.RotationVariance);

		p.alphaBlendTime = (1.0f - config.AlphaBlend) * p.timeToLive;
}
